
#include "LabviewRecoProcessor.hh"

//lcio
#include "IMPL/LCCollectionVec.h"
#include "IMPL/CalorimeterHitImpl.h"

//marlin
#include "marlin/ConditionsProcessor.h"
#include "marlin/Exceptions.h"

//calice_userlib
#include "HBUMapper.hh"
#include "LabviewBlock.hh"


namespace CALICE{


  LabviewRecoProcessor aLabviewRecoProcessor ;

  
  LabviewRecoProcessor::LabviewRecoProcessor() : Processor("LabviewRecoProcessor")
  {
    
    _description = "Reads the EPT labview raw data files, and E4DMapper to do reconstruction." ;

    registerInputCollection( LCIO::LCGENERICOBJECT,
                             "E4DMapperCollection" ,
                             "Name of the E4DMapper collection"  ,
                             _E4DMapperColName ,
                             std::string("E4DMapper") ) ;
    
    registerProcessorParameter("InputCollectionName",
                               "Name of the input collection of Labview raw data to be checked",
                               _inputColName,
                               std::string("LabviewData"));
    
    registerProcessorParameter("OutputAhcHitCollectionName",
                               "Name of the output AHCal hit collection, of type CalorimeterHit",
                               _ahcHitOutputColName,
                               std::string("EPTCalorimeter_Hits"));
    
  }
  
   LabviewRecoProcessor::~LabviewRecoProcessor()
  {
    //no op!
  }
  
  void LabviewRecoProcessor::init() {    
    // printParameters() ;    

    std::stringstream message;
    bool error = false;

    _E4DMapperChanged  = false;

    if (!ConditionsProcessor::registerChangeListener(this, _E4DMapperColName))
      {
        message << " undefined conditions: " << _E4DMapperColName << std::endl;
        error = true;
      }
 
  }


  /***************************************************************************************/
  /* function to listen for condition changes in LCCD                                    */
  /*                                                                                     */
  /* should only remember pointer of collection and set flag for the changed conditions; */
  /* processing should wait till process event. this ensures that constants that depend  */
  /* on two different conditions collections have both collections available             */
  /*                                                                                     */
  /* the callbacks from LCCD have no guarantied order                                    */
  /*                                                                                     */
  /***************************************************************************************/
  void LabviewRecoProcessor::conditionsChanged( LCCollection * col ) {

    std::string colName = col->getParameters().getStringVal("CollectionName") ;

    if (colName == _E4DMapperColName)
      {
        _E4DMapperCol = col;
        _E4DMapperChanged = true;
      } else {
      
      m_out(ERROR) << "Called as conditions listener for collection " << colName << ", but not responsible." << std::endl;
      throw StopProcessingException(this);
    }

  }
     

  /***************************************************************************************/
  /*                                                                                     */
  /*                                                                                     */
  /* functions to do the actual update (or initialisation) of the calibrations           */
  /*                                                                                     */
  /*                                                                                     */
  /***************************************************************************************/
 
  void LabviewRecoProcessor::updateE4DMapper()
  {
    if (!_E4DMapperCol)
      {
        m_out(ERROR) << "Cannot update E4DMapper, E4DMapper collection is not valid." << std::endl;
        throw StopProcessingException(this);
      }

      int _HBUNumber = 1;
      
      for ( int i = 0; i != _E4DMapperCol->getNumberOfElements(); ++i ) 
	{     

	  CALICE::HBUMapper hbum( _E4DMapperCol->getElementAt( i ) );
	  
	  _e4dmapping.createMapbyMCCIJK(_HBUNumber,
				       hbum.getChip(),
				       hbum.getChannel(),
				       hbum.getI(),
				       hbum.getJ(),
				       hbum.getK());
	  

	  //_e4dmapping.createMapbyMCCIJK(1,0,0,6,19,1);
	  std::cout<< hbum << std::endl;

	}
      
      _E4DMapperChanged = false;

      // Print it out on the screen.
      _e4dmapping.printMap();
      std::cout<<"======= ModuleDescription ========="<<std::endl;
     _e4dmapping.printModuleDescription();
      
  }

 
  /***************************************************************************************/
  /*                                                                                     */
  /*                                                                                     */
  /* functions to do  the calibrations for each event, and write out the ture hits       */
  /*                                                                                     */
  /*                                                                                     */
  /***************************************************************************************/

  void LabviewRecoProcessor::processEvent(LCEvent* evt){

    if (_E4DMapperChanged )  updateE4DMapper();



    LCCollection* inputCol = NULL;


    try {
      //fetch Labview data raw collection
      inputCol = evt->getCollection( _inputColName ) ;

    } catch (  lcio::DataNotAvailableException &err ) {
      err.what();
      delete inputCol;
      return;
    }

    int noElem = inputCol->getNumberOfElements();
    if (noElem <= 0) return;

    m_out(DEBUG0)<<" \n\n=========================Start to process event "<<evt->getEventNumber()<<endl;
    m_out(DEBUG0)<<"inputcol "<<_inputColName<<" has "<<noElem<<" hits"<<endl;

    LCCollection *ahcHitOutputCol  = new LCCollectionVec(LCIO::CALORIMETERHIT);
    //we want to save the position, and this can only be done if the CHBIT_LONG bit is set
    ahcHitOutputCol->setFlag(ahcHitOutputCol->getFlag() | 1 << LCIO::CHBIT_LONG );
    ahcHitOutputCol->setFlag(ahcHitOutputCol->getFlag() | 1 << LCIO::RCHBIT_TIME );
    ahcHitOutputCol->setFlag(ahcHitOutputCol->getFlag() | 1 << LCIO::RCHBIT_ENERGY_ERROR );


    //First version write out all raw informationinto carlorimeterHit, 
    for (unsigned int ielm=0; ielm < static_cast<unsigned int>(noElem); ielm++) {
      
      //LCObject *obj = col->getElementAt(ielm);
      const LabviewBlock _lBlock(inputCol->getElementAt(ielm));
      
      
      if ( _lBlock.GetHitBit() == 1)
	{      
	  int iTDC  = _lBlock.GetTDC();
	  int iADC  = _lBlock.GetADC();
	  
	  int module  = 1;
	  int chip    = _lBlock.GetChipID();
	  int channel = _lBlock.GetChannel();
	  
	  int iI = static_cast<int>(_e4dmapping.getIFromModuleChipChannel(module, chip, channel));
	  int iJ = static_cast<int>(_e4dmapping.getJFromModuleChipChannel(module, chip, channel));
      
	  int cellID0 = static_cast<int>(_e4dmapping.getCellID0FromModuleChipChannel(module, chip, channel));
	  
	  int iXPos = (iI-1)*30+15;
	  int iYPos = (iJ-1)*30+15;
	  int iZPos = 1000;
	  
	  float newPosition[3] = {static_cast<float>(iXPos),static_cast<float>(iYPos),static_cast<float>(iZPos) };
	  float energy = static_cast<float>(iADC);
	  float energyError = 0.0;
	  float time = static_cast<float>(iTDC);
	  
	  CalorimeterHitImpl *newHit = new CalorimeterHitImpl();	  
	  newHit->setEnergy(energy);
	  newHit->setEnergyError(energyError);
	  newHit->setPosition(newPosition);
	  newHit->setCellID0(cellID0);
	  newHit->setTime(time);
	  
	  ahcHitOutputCol->addElement(newHit);
	}

    }
 

    if (ahcHitOutputCol->getNumberOfElements() > 0)
      {
	
	DecoderSet decoder = _e4dmapping.getDecoder();
	std::string  outputEncodingString = decoder.getCellIDEncoding();
	LCParameters &param = ahcHitOutputCol->parameters();
	param.setValue(LCIO::CellIDEncoding, outputEncodingString);
	
	evt->addCollection( ahcHitOutputCol, _ahcHitOutputColName.c_str() );
      } else {delete ahcHitOutputCol;}

  }
  

  /***************************************************************************************/
  /*                                                                                     */
  /*                                                                                     */
  /*                          end function                                               */
  /*                                                                                     */
  /*                                                                                     */
  /***************************************************************************************/

  void LabviewRecoProcessor::end() {    
    m_out(MESSAGE) <<" ===== End of LabviewRecoProcessor processor ===== " << std::endl;
  }

} //namespace

